# Python3 错误和异常

"""
Python 有两种错误很容易辨认：语法错误和异常。
"""

# 语法错误

"""
Python 的语法错误或者称之为解析错，是初学者经常碰到的，如下实例

>>> while True print('Hello world')
  File "<stdin>", line 1, in ?
    while True print('Hello world')
                   ^
SyntaxError: invalid syntax

"""

# 异常

"""
异常是程序在运行过程中出现的错误，Python 有很多内置的异常，比如：

ZeroDivisionError：被零除
ValueError：无效的运算
IOError：输入输出错误

"""

# 异常处理

"""
try 语句按照如下方式工作；

首先，执行 try 子句（在关键字 try 和关键字 except 之间的语句）
如果没有异常发生，忽略 except 子句，try 子句执行后结束。
如果在执行 try 子句的过程中发生了异常，那么 try 子句余下的部分将被忽略。如果异常的类型和 except 之后的名称相符，那么对应的 except 子句将被执行。最后执行 try 语句之后的代码。
如果一个异常没有与任何的 except 匹配，那么这个异常将会传递给上层的 try 中。
一个 try 语句可能包含多个 except 子句，分别来处理不同的特定的异常。最多只有一个分支会被执行。

处理程序将只针对对应的 try 子句中的异常进行处理，而不是其他的 try 的处理程序中的异常。

一个 except 子句可以同时处理多个异常，这些异常将被放在一个括号里成为一个元组，例如:

>>> except (RuntimeError, TypeError, NameError):
        pass

"""

while True:
    try:
        x = int(input("Please enter a number: "))
        break
    except ValueError:
        print("Oops!  That was no valid number.  Try again   ")

"""
最后一个 except 子句可以忽略异常的名称，它将被当作通配符使用。你可以使用这种方法打印一个错误信息，然后再次把异常抛出
"""

import sys

try:
    f = open('myfile.txt')
    s = f.readline()
    i = int(s.strip())
except OSError as err:
    print("OS error: {0}".format(err))
except ValueError:
    print("Could not convert data to an integer.")
except:
    print("Unexpected error:", sys.exc_info()[0])
    raise

"""
try except 语句还有一个可选的 else 子句，如果使用这个子句，那么必须放在所有的 except 子句之后。
这个子句将在 try 子句没有发生任何异常的时候执行。例如:
"""
for arg in sys.argv[1:]:
    try:
        f = open(arg, 'r')
    except IOError:
        print('cannot open', arg)
    else:
        print(arg, 'has', len(f.readlines()), 'lines')
        f.close()

# 抛出异常

"""
Python 的 raise 语句允许你抛出（或生成）自己的异常。

raise 唯一的一个参数指定了要被抛出的异常。它必须是一个异常的实例或者是异常的类（也就是 Exception 的子类）。

如果你只想知道这是否抛出了一个异常，并不想去处理它，那么一个简单的 raise 语句就可以再次把它抛出。

>>> raise NameError('HiThere')
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
NameError: HiThere

"""

# raise NameError('HiThere')

# 用户自定义异常

"""
你可以通过创建一个新的 exception 类来拥有自己的异常。异常应该继承自 Exception 类，或者直接继承，或者间接继承，例如:
"""

class MyError(Exception):
    def __init__(self, value):
        self.value = value

    def __str__(self):
        return repr(self.value)


try:
    raise MyError(2 * 2)
except MyError as e:
    print('My exception occurred, value:', e.value)

# 定义清理行为

"""
try 语句还有另外一个可选的子句，它定义了无论在任何情况下都会执行的清理行为。 例如:
"""

try:
    raise KeyboardInterrupt
finally:
    print('Goodbye, world!')

# 预定义的清理行为

"""
一些对象定义了标准的清理行为，无论系统是否成功的使用了它，一旦不需要它了，那么这个标准的清理行为就会执行。

for line in open("myfile.txt"):
    print(line, end="")

以上这段代码的问题是，当执行完毕后，文件会保持打开状态，并没有被关闭。

关键词 with 语句就可以保证诸如文件之类的对象在使用完之后一定会正确的执行他的清理方法:

with open("myfile.txt") as f:
    for line in f:
        print(line, end="")

"""




